package redisdeploylogic

import (
	"context"
	"errors"
	"fmt"
	"github.com/zeromicro/go-zero/core/logx"
	"strings"
	"sync"
	"time"
	"yunzhan/common/models"
	utils "yunzhan/common/utils"
	"yunzhan/rpc-server/internal/svc"
	agent "yunzhan/rpc-server/pb"
)

type DeployRedisLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewDeployRedisLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeployRedisLogic {
	return &DeployRedisLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

// DeployRedis 单机部署
func (l *DeployRedisLogic) DeployRedis(in *agent.DeployRedisRequest) (*agent.DeployResponse, error) {
	if in.NodeInfo == nil {
		return &agent.DeployResponse{
			Code:    500,
			Message: "未配置节点信息, 无法执行Redis部署",
		}, errors.New("未配置节点信息, 无法执行Redis部署")
	}

	resp := &agent.DeployResponse{
		Code:    200,
		Message: fmt.Sprintf("请求已接收，正在部署 %s 到节点 %s", in.ConfigInfo.ComponentName, in.NodeInfo.Host),
	}

	logx.Infof("集群id: %s", in.ClusterID)
	go l.startDeployment(in)

	return resp, nil
}
func (l *DeployRedisLogic) startDeployment(in *agent.DeployRedisRequest) {

	var wg sync.WaitGroup

	wg.Add(1)
	go func() {
		defer wg.Done()

		// 创建 LogManager 实例
		logManager := models.NewLogManager(100, fmt.Sprintf("http://%s:%d", l.svcCtx.Config.RestServices["apiServer"].RestConf.Host, l.svcCtx.Config.RestServices["apiServer"].RestConf.Port))
		defer logManager.Close()

		flushInterval := 5 * time.Second // 刷新间隔
		maxBatchSize := 100              // 最大批量大小
		retryDelay := 2 * time.Second    // 重试延迟
		maxRetries := 3                  // 最大重试次数

		// 创建 ComponentInfoManager 实例
		componentInfoManager := models.NewComponentInfoManager(
			1000,
			fmt.Sprintf("http://%s:%d", l.svcCtx.Config.RestServices["apiServer"].RestConf.Host, l.svcCtx.Config.RestServices["apiServer"].RestConf.Port),
			flushInterval,
			maxBatchSize,
			retryDelay,
			maxRetries)
		defer componentInfoManager.Close()
		packageDirectory := in.ConfigInfo.PackagePath
		rtrimDir := utils.Rtrim(packageDirectory, "/")
		redisVersion := in.ConfigInfo.ComponentName

		installPath := utils.Rtrim(in.ConfigInfo.InstallPath, "/")
		baseInfo := fmt.Sprintf("安装包存放目录: %s, Redis version: %s, Redis 安装目录: %s", rtrimDir, redisVersion, installPath)
		step := 1
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", baseInfo, "Redis", "Server")

		checkPathCommand := fmt.Sprintf(`
if [ ! -d "%s" ]; then
   sudo mkdir -p "%s"
fi
`, installPath, installPath)

		step++
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "执行组件安装路径检查...", "Redis", "Server")
		output, err := utils.ExecCommand(checkPathCommand)
		if err != nil {
			message := fmt.Sprintf("检查组件安装路径异常: %v, output: %s", err, output)
			logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", message, "Redis", "Server")
			return
		}
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "检查组件安装路径完成", "Redis", "Server")

		step++
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "解压安装包...", "Redis", "Server")

		tarCommand := fmt.Sprintf("sudo tar -xzf %s/%s -C %s", rtrimDir, redisVersion, installPath)
		output, err = utils.ExecCommand(tarCommand)
		if err != nil {
			message := fmt.Sprintf("解压安装包异常: %v, output: %s", err, output)
			logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", message, "Redis", "Server")
			return
		}
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "解压安装包完成", "Redis", "Server")

		redisPackageName := utils.Rtrim(redisVersion, ".tar.gz")
		redisHome := fmt.Sprintf("%s/%s", installPath, redisPackageName)

		step++
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "修改组件所属用户组...", "Redis", "Server")
		chownCommand := fmt.Sprintf("sudo chown -R %s:%s %s", in.NodeInfo.Username, in.NodeInfo.Username, redisHome)
		output, err = utils.ExecCommand(chownCommand)
		if err != nil {
			message := fmt.Sprintf("修改组件所属用户组异常: %v, output: %s", err, output)
			logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", message, "Redis", "Server")
			return
		}
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "修改组件所属用户组完成", "Redis", "Server")

		checkPathCommand = fmt.Sprintf(`
if [ ! -d "%s" ]; then
   sudo mkdir -p "%s"
fi
sudo chown -R %s:%s %s
`, in.RedisConfig.Dir, in.RedisConfig.Dir, in.NodeInfo.Username, in.NodeInfo.Username, in.RedisConfig.Dir)
		step++
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "检查组件数据存储目录...", "Redis", "Server")
		output, err = utils.ExecCommand(checkPathCommand)
		if err != nil {
			message := fmt.Sprintf("检查组件数据存储目录异常: %v, output: %s", err, output)
			logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", message, "Redis", "Server")
			return
		}
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "检查组件数据存储目录完成", "Redis", "Server")
		step++
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "备份redis.conf文件...", "Redis", "Server")
		cpCommand := fmt.Sprintf("cp -r %s/conf/redis.conf %s/conf/redis.conf.bak", redisHome, redisHome)
		output, err = utils.ExecCommand(cpCommand)
		if err != nil {
			message := fmt.Sprintf("备份redis.conf文件异常: %v, output: %s", err, output)
			logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", message, "Redis", "Server")
			return
		}
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "备份redis.conf配置文件完成", "Redis", "Server")

		redisConfig := generateRedisConfig(in.RedisConfig, redisHome, in.NodeInfo.Host)

		step++
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "配置redis.conf文件...", "Redis", "Server")
		vimCommand := fmt.Sprintf("echo '%s' %s/conf/redis.conf", redisConfig, redisHome)
		output, err = utils.ExecCommand(vimCommand)
		if err != nil {
			message := fmt.Sprintf("配置redis.conf文件异常: %v, output: %s", err, output)
			logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", message, "Redis", "Server")
			return
		}
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "配置redis.conf文件完成", "Redis", "Server")

		redisServiceFile := fmt.Sprintf(`[Unit]
Description=Redis In-Memory Data Store
Documentation=https://redis.io/documentation
After=network.target

[Service]
Type=forking
User=%s
Group=%s
ExecStart=%s/bin/redis-server %s/conf/redis.conf
ExecReload=%s/bin/redis-cli -p %s -a %s config rewrite
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true
Restart=on-failure
LimitNOFILE=65536
LimitMEMLOCK=infinity

[Install]
WantedBy=multi-user.target`, in.NodeInfo.Username, in.NodeInfo.Username, redisHome, redisHome, redisHome, in.NodeInfo.Host, in.RedisConfig.RequirePass)
		step++
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "创建redis.service文件...", "Redis", "Server")
		writeServiceFileCommand := fmt.Sprintf("echo '%s' | sudo tee /etc/systemd/system/redis.service", redisServiceFile)
		output, err = utils.ExecCommand(writeServiceFileCommand)
		if err != nil {
			message := fmt.Sprintf("创建 redis.service 文件异常: %v, output: %s", err, output)
			logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", message, "Redis", "Server")
			return
		}
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "创建redis.service文件完成", "Redis", "Server")
		step++
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "启动Redis服务...", "Redis", "Server")
		reloadSystemdCommand := fmt.Sprintf("sudo systemctl daemon-reload && sudo systemctl enable redis.service --now")
		output, err = utils.ExecCommand(reloadSystemdCommand)
		if err != nil {
			message := fmt.Sprintf("启动Redis服务异常: %v, output: %s", err, output)
			logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", message, "Redis", "Server")
			return
		}
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "启动Redis服务完成", "Redis", "Server")

		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "等待Redis服务完全启动", "Redis", "Server")

		time.Sleep(3 * time.Second)
		step++
		statusCommand := fmt.Sprintf("sudo systemctl is-active redis.service")
		output, err = utils.ExecCommand(statusCommand)
		if err != nil || strings.TrimSpace(output) != "active" {
			message := fmt.Sprintf("Redis启动后未正常运行: %v, output: %s", err, output)
			logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", message, "Redis", "Server")
			return
		}
		logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", "Redis部署成功", "Redis", "Server")

		currentIP, currentIPErr := utils.GetCurrentInternalIP()
		if err != nil {
			message := fmt.Sprintf("%s: 无法获取当前主机IP: %v", in.NodeInfo.Host, currentIPErr)
			logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", message, "Redis", "Server")
			return
		}
		currentHost, currentHostErr := utils.GetCurrentHostname()
		if err != nil {
			message := fmt.Sprintf("%s: 无法获取当前主机名: %v", in.NodeInfo.Host, currentHostErr)
			logManager.SendLog(in.ClusterID, in.NodeInfo.Host, step, "INFO", message, "Redis", "Server")
			return
		}

		requirePass := ""
		if in.RedisConfig.RequirePass != "" {
			requirePass, err = utils.Encrypt([]byte("68656c6c6f2077dg"), in.RedisConfig.RequirePass)
			if err != nil {
				logx.Errorf("密码加密异常: %v", err)
			}
		}

		redisInfo := &models.ComponentInfo{
			ClusterID:      in.ClusterID,
			ComponentName:  "Redis",
			Version:        in.Version,
			NodeHost:       currentHost,
			NodeIP:         currentIP,
			ComponentRole:  "Server",
			HomePath:       redisHome,
			DataStorageDir: in.RedisConfig.Dir,
			Port:           string(in.RedisConfig.Port),
			Status:         true,
			AdditionalInfo: fmt.Sprintf("config_file=%s/conf/redis.conf,require_pass=%s", redisHome, requirePass),
		}

		componentInfoManager.SendComponentInfo(redisInfo)

	}()

	wg.Wait()

}

func generateRedisConfig(config *agent.RedisConfig, redisHome, host string) string {
	var configBuilder strings.Builder

	configBuilder.WriteString(fmt.Sprintf("bind %s\n", host))

	if config.Port != 0 {
		configBuilder.WriteString(fmt.Sprintf("port %d\n", config.Port))
	}

	if config.Daemonize != "" {
		configBuilder.WriteString(fmt.Sprintf("daemonize %s\n", config.Daemonize))
	}

	configBuilder.WriteString(fmt.Sprintf("pidfile %s/redis-%d.pid\n", redisHome, config.Port))

	if config.ProtectedMode != "" {
		configBuilder.WriteString(fmt.Sprintf("protected-mode %s\n", config.ProtectedMode))
	}

	if config.Loglevel != "" {
		configBuilder.WriteString(fmt.Sprintf("loglevel %s\n", config.Loglevel))
	}

	configBuilder.WriteString(fmt.Sprintf("logfile %s/logs/redis-%d.log\n", redisHome, config.Port))

	if config.Databases != 0 {
		configBuilder.WriteString(fmt.Sprintf("databases %d\n", config.Databases))
	}

	if config.Save != nil && len(config.Save) > 0 {
		for key, value := range config.Save {
			configBuilder.WriteString(fmt.Sprintf("save %s %d\n", key, value))
		}
	}

	if config.StopWritesOnBgSaveError != "" {
		configBuilder.WriteString(fmt.Sprintf("stop-writes-on-bgsave-error %s\n", config.StopWritesOnBgSaveError))
	}

	if config.RdbCompression != "" {
		configBuilder.WriteString(fmt.Sprintf("rdbcompression %s\n", config.RdbCompression))
	}

	if config.RdbChecksum != "" {
		configBuilder.WriteString(fmt.Sprintf("rdbchecksum %s\n", config.RdbChecksum))
	} else {
		configBuilder.WriteString(fmt.Sprintf("rdbchecksum no\n"))
	}

	if config.DbFilename != "" {
		configBuilder.WriteString(fmt.Sprintf("dbfilename %s\n", config.DbFilename))
	} else {
		configBuilder.WriteString(fmt.Sprintf("dbfilename dump.rdb\n"))
	}

	if config.Dir != "" {
		configBuilder.WriteString(fmt.Sprintf("dir %s\n", config.Dir))
	}

	if config.SlaveServeStaleData != "" {
		configBuilder.WriteString(fmt.Sprintf("slave-serve-stale-data %s\n", config.SlaveServeStaleData))
	}

	if config.SlaveReadOnly != "" {
		configBuilder.WriteString(fmt.Sprintf("slave-read-only %s\n", config.SlaveReadOnly))
	}

	if config.ReplDiskLessSync != "" {
		configBuilder.WriteString(fmt.Sprintf("repl-diskless-sync %s\n", config.ReplDiskLessSync))
	}

	if config.ReplDiskLessSyncDelay != "" {
		configBuilder.WriteString(fmt.Sprintf("repl-diskless-sync-delay %s\n", config.ReplDiskLessSyncDelay))
	}

	if config.SlavePriority != "" {
		configBuilder.WriteString(fmt.Sprintf("slave-priority %s\n", config.SlavePriority))
	}

	if config.AppendOnly != "" {
		configBuilder.WriteString(fmt.Sprintf("appendonly %s\n", config.AppendOnly))
		configBuilder.WriteString(fmt.Sprintf("appendfsync %s\n", config.Appendfsync))
	}

	if config.Timeout != "" {
		configBuilder.WriteString(fmt.Sprintf("timeout %s\n", config.Timeout))
	}

	if config.MaxMemory != "" {
		configBuilder.WriteString(fmt.Sprintf("maxmemory %s\n", config.MaxMemory))
	}

	if config.RequirePass != "" {
		configBuilder.WriteString(fmt.Sprintf("requirepass %s\n", config.RequirePass))
	}

	return configBuilder.String()
}
